package com.quick.develop.flowable.service.task.impl;

import com.quick.develop.flowable.enums.BpmTaskCategory;
import com.quick.develop.framework.utils.StringUtils;
import com.quick.develop.framework.security.utils.SecurityUtils;
import com.quick.develop.flowable.framework.factory.FlowServiceFactory;
import com.quick.develop.flowable.service.definition.IBpmProcessDefinitionService;
import com.quick.develop.flowable.service.process.IBpmProcessInstanceService;
import com.quick.develop.flowable.service.task.IBpmPermissionService;
import com.quick.develop.flowable.service.task.IBpmTaskService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.Objects;
import java.util.Optional;

import static com.quick.develop.flowable.enums.BpmErrorCodeConstants.*;
import static com.quick.develop.framework.common.utils.ServiceExceptionUtils.exception;

/**
 * @author junyuan.liu
 * @description: 
 * @date 2022/7/4 14:40
 */
@Service
@Slf4j
@Validated
public class BpmPermissionServiceImpl extends FlowServiceFactory implements IBpmPermissionService {
    @Resource
    private IBpmTaskService bpmTaskService;
    
    @Resource
    private IBpmProcessDefinitionService bpmProcessDefinitionService;
    
    @Resource
    private IBpmProcessInstanceService bpmProcessInstanceService;

    @Override
    public Task checkTask(String taskId) {
        return this.checkTask(taskId, SecurityUtils.getUserCode());
    }

    @Override
    public Task checkTask(String taskId, String userId) {
        String userCode = StringUtils.isEmpty(userId) ? SecurityUtils.getUserCode() : userId;
        if (StringUtils.isEmpty(taskId)) {
            throw exception(TASK_IS_EMPTY_ID);
        }
        Task task = bpmTaskService.getTaskById(taskId);
        Optional.ofNullable(task).orElseThrow(() -> exception(TASK_HANDLER_FAIL_NOT_EXISTS));
        if (task.isSuspended()) {
            throw exception(TASK_IS_SUSPENDED);
        }
        boolean isAdmin = SecurityUtils.isAdmin();
        if (!isAdmin) {
            if (StringUtils.isEmpty(task.getAssignee())) {
                if (StringUtils.isNotEmpty(task.getDescription()) && !task.getDescription().contains(BpmTaskCategory.CANDIDATE.getCode())) {
                    throw exception(TASK_HANDLER_FAIL_ASSIGN_NOT_SELF);
                }
            }
            else if (!Objects.equals(userCode, task.getAssignee()) ) {
                throw exception(TASK_HANDLER_FAIL_ASSIGN_NOT_SELF);
            }
        }
        
        return task;
    }

    @Override
    public Model checkModel(String id) {
        Model model = repositoryService.getModel(id);
        Optional.ofNullable(model).orElseThrow(()-> exception(MODEL_NOT_EXISTS));
        
        return model;
    }
    
    @Override
    public ProcessDefinition checkProcessDefinitionByDeploymentId(String id) {
        ProcessDefinition definition = bpmProcessDefinitionService.getProcessDefinitionByDeploymentId(id);
        Optional.ofNullable(definition).orElseThrow(()-> exception(PROCESS_DEFINITION_NOT_EXISTS));
        
        return definition;
    }

    @Override
    public void isAdminOnException() {
        if (!SecurityUtils.isAdmin()) {
            throw exception(USER_NOT_SUPER_ADMIN_ERROR);
        }
    }

    @Override
    public ProcessInstance runProcessNotExistOnException(String id) {
        ProcessInstance instance = bpmProcessInstanceService.getProcessInstance(id);
        Optional.ofNullable(instance).orElseThrow(() -> exception(PROCESS_INSTANCE_NOT_EXISTS));
        return instance;
    }
}
